#include "bsp_st7789.h"
#include "bsp_i2c.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#define TAG "st7789"
#define LCD_HOST SPI3_HOST
spi_device_handle_t hspi3;
DRAM_ATTR uint8_t gram[320 * 2];

void HAL_LCD_Set_Window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);

// This function is called (in irq context!) just before a transmission starts. It will
// set the D/C line to the value indicated in the user field.
void lcd_spi_pre_transfer_callback(spi_transaction_t *t)
{
    gpio_set_level(BSP_LCD_DC, (int)t->user);
}

void back_light_onoff(uint8_t onoff)
{
    if (onoff == ON)
    {
        gpio_set_level(BSP_LCD_BACKLIGHT, 0); // turn on
    }
    else
    {
        gpio_set_level(BSP_LCD_BACKLIGHT, 1); // turn off
    }
}

void back_light_init()
{
    // bklight turn on
    ESP_LOGI(TAG, " backlight init 41");
    gpio_config_t bk_gpio_config = {
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = 1,
        .pin_bit_mask = 1ULL << BSP_LCD_BACKLIGHT,
    };
    ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
    back_light_onoff(ON);
}

void dc_init()
{
    // bklight turn on
    ESP_LOGI(TAG, "dc int 39");
    gpio_config_t dc_gpio_config = {
        .mode = GPIO_MODE_OUTPUT,
        .pin_bit_mask = 1ULL << BSP_LCD_DC,
    };
    gpio_config(&dc_gpio_config);
}

void lcd_dc(uint8_t dc)
{
    gpio_set_level(BSP_LCD_DC, dc);
}

void spi3_init()
{
    // spi init
    ESP_LOGI(TAG, "Initialize SPI bus");
    spi_bus_config_t buscfg = {
        .sclk_io_num = BSP_LCD_SPI_CLK,  // 10, //
        .mosi_io_num = BSP_LCD_SPI_MOSI, // 11, //
        .miso_io_num = -1,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = BSP_LCD_H_RES * 16 * 2 + 8,
    };
    ESP_LOGI(TAG, "SPI INIT 40 41.");
    ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));
    spi_device_interface_config_t dev_config = {

        .clock_speed_hz = 26 * 1000 * 1000, // 26Mhz
        .mode = 0,                          // SPI mode 2
        .spics_io_num = -1,
        .queue_size = 7,
        .pre_cb = lcd_spi_pre_transfer_callback,
    };
    spi_bus_add_device(LCD_HOST, &dev_config, &hspi3);
}

void spi3_transmit(uint8_t cmd, uint8_t *data, uint32_t length)
{
    spi_transaction_t t = {
        .length = length * 8,
        .tx_buffer = data,
        .user = (void *)cmd,

        .addr = 0,
        .rx_buffer = NULL,
        .flags = 0,
        .cmd = 0,
        .rxlength = 0,
    };
    spi_device_polling_transmit(hspi3, &t); // Transmit!
}

void HAL_LCD_WriteReg(uint8_t reg)
{
    lcd_cs(0);
    spi3_transmit(0, &reg, 1);
    lcd_cs(1);
}
void HAL_LCD_WriteData8(uint8_t data)
{

    lcd_cs(0);
    spi3_transmit(1, &data, 1);
    lcd_cs(1);
}

void HAL_LCD_WriteData(uint8_t *data, uint32_t length)
{
    lcd_cs(0);
    spi3_transmit(1, data, length);
    lcd_cs(1);
}

void st7789_reg_init()
{

    vTaskDelay(100 / portTICK_PERIOD_MS);
// DRAM_ATTR static const lcd_init_cmd_t st_init_cmds[] = {
/* Memory Data Access Control, MX=MV=1, MY=ML=MH=0, RGB=0 */
#define MY 7
#define MX 6
#define MV 5
#define ML 4
#define RGB 3
#define MH 2

    /* Sleep out */
    HAL_LCD_WriteReg(0x11);
    vTaskDelay(120);
    HAL_LCD_WriteReg(0x36);
    HAL_LCD_WriteData8(0 << MY | 1 << MX | 1 << MV | 1 << ML | 0 << RGB | 0 << MH);
    /* Interface Pixel Format, 16bits/pixel for RGB/MCU interface */
    HAL_LCD_WriteReg(0x3A);
    HAL_LCD_WriteData8(0X05);
    // /* Porch Setting */
    HAL_LCD_WriteReg(0xB2);
    uint8_t b2[5] = {0x0c, 0x0c, 0x00, 0x33, 0x33};
    HAL_LCD_WriteData(b2, 5);
    // /* Gate Control, Vgh=13.65V, Vgl=-10.43V */
    HAL_LCD_WriteReg(0xB7);
    HAL_LCD_WriteData8(0X45);
    // /* VCOM Setting, VCOM=1.175V */
    HAL_LCD_WriteReg(0xBB);
    HAL_LCD_WriteData8(0X2B);
    // /* LCM Control, XOR: BGR, MX, MH */
    HAL_LCD_WriteReg(0xC0);
    HAL_LCD_WriteData8(0X2C);
    // /* VDV and VRH Command Enable, enable=1 */
    HAL_LCD_WriteReg(0xC2);
    HAL_LCD_WriteData8(0X01);
    HAL_LCD_WriteData8(0XFF);
    // /* VRH Set, Vap=4.4+... */
    HAL_LCD_WriteReg(0xC3);
    HAL_LCD_WriteData8(0X011);
    // /* VDV Set, VDV=0 */
    HAL_LCD_WriteReg(0xC4);
    HAL_LCD_WriteData8(0X20);
    // /* Frame Rate Control, 60Hz, inversion=0 */
    HAL_LCD_WriteReg(0xC6);
    HAL_LCD_WriteData8(0X0F);
    // /* Power Control 1, AVDD=6.8V, AVCL=-4.8V, VDDS=2.3V */
    HAL_LCD_WriteReg(0xD0);
    HAL_LCD_WriteData8(0XA4);
    HAL_LCD_WriteData8(0XA1);
    // /* Positive Voltage Gamma Control */
    uint8_t E0[14] = {0xD0, 0x00, 0x05, 0x0E, 0x15, 0x0D, 0x37, 0x43, 0x47, 0x09, 0x15, 0x12, 0x16, 0x19};
    HAL_LCD_WriteReg(0xE0);
    HAL_LCD_WriteData(E0, 14);
    // /* Negative Voltage Gamma Control */
    uint8_t E1[14] = {0xD0, 0x00, 0x05, 0x0D, 0x0C, 0x06, 0x2D, 0x44, 0x40, 0x0E, 0x1C, 0x18, 0x16, 0x19};
    HAL_LCD_WriteReg(0xE1);
    HAL_LCD_WriteData(E1, 14);

    HAL_LCD_WriteReg(0x21); /* 20h Display Inversion Off; INVON (21h): Display Inversion On */
    // /* Display On */
    HAL_LCD_WriteReg(0x29);
    HAL_LCD_Set_Window(0, 0, 320 - 1, 240 - 1);
};

void st7789_init(void)
{
    back_light_init();
    dc_init();
    spi3_init();
    st7789_reg_init();
    HAL_LCD_Clear_fill(WHITE);
}

// 设置液晶屏颜色
void lcd_set_color(uint16_t color)
{
    HAL_LCD_Clear_fill(color);
}

void HAL_LCD_Set_Window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{

    HAL_LCD_WriteReg(0x2A);
    HAL_LCD_WriteData8(x0 >> 8);
    HAL_LCD_WriteData8(x0 & 0XFF);

    HAL_LCD_WriteData8(x1 >> 8);
    HAL_LCD_WriteData8(x1 & 0XFF);

    HAL_LCD_WriteReg(0X2B);
    HAL_LCD_WriteData8(y0 >> 8);
    HAL_LCD_WriteData8(y0 & 0XFF);

    HAL_LCD_WriteData8(y1 >> 8);
    HAL_LCD_WriteData8(y1 & 0XFF);

    HAL_LCD_WriteReg(0X2C);
}

void HAL_LCD_SPI_Pix(uint16_t x, uint16_t y, uint16_t color)
{
    HAL_LCD_WriteReg(0x2A);
    HAL_LCD_WriteData8(x >> 8);
    HAL_LCD_WriteData8(x & 0XFF);
    HAL_LCD_WriteReg(0X2B);
    HAL_LCD_WriteData8(y >> 8);
    HAL_LCD_WriteData8(y & 0XFF);
    HAL_LCD_WriteReg(0X2C);

    HAL_LCD_WriteData8((uint8_t)(color >> 8));
    HAL_LCD_WriteData8((uint8_t)color);
}

// 设置GRAM 指针
void HAL_LCD_SetCursor(uint16_t Xpos, uint16_t Ypos)
{
    HAL_LCD_WriteReg(0x2A);
    HAL_LCD_WriteData8(Xpos >> 8);
    HAL_LCD_WriteData8(Xpos & 0XFF);
    HAL_LCD_WriteReg(0X2B);
    HAL_LCD_WriteData8(Ypos >> 8);
    HAL_LCD_WriteData8(Ypos & 0XFF);
}

// 目标区域填充
void HAL_LCD_Clear_fill(uint16_t color)
{
    for (int j = 0; j < 320 * 2; j += 2)
    {
        gram[j] = (color >> 8) & 0xff;
        gram[j + 1] = (color) & 0xff;
    }

    HAL_LCD_Set_Window(0, 0, 320 - 1, 240 - 1);
    lcd_cs(0);
    for (int i = 0; i < 240; i++)
    {
        spi3_transmit(1, gram, 640);
    }
    lcd_cs(1);
}

// 目标区域填充
void HAL_LCD_fill(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t *data)
{
    HAL_LCD_Set_Window(x0, y0, x1, y1);
    uint32_t line_length = (x1 - x0 + 1) * 2;
    lcd_cs(0);
    for (int i = y0; i <= y1; i++)
        spi3_transmit(1, &data[(i - y0) * line_length], line_length);
    lcd_cs(1);
}